/*
* Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const { X2DBeta } = require("../../../engine/graphics/X2DInterface");
const { Scr } = require("../../../engine/XDefine");
const { TextEditor } = require("../../../engine/XTextInput");
const { XTools } = require("../../../engine/XTools");
const { AttributeArea } = require("../../AttributeArea");
const { EnvManager } = require("../EnvManager");
const { ProjectConfig } = require("../ProjectConfig");
const { CustomNode } = require("./CustomNode");
const { CustomMessage } = require("./CustomMessage");
const { DefaultNodeCode } = require("./DefaultNodeCode");
const { LogManager } = require("../LogManager");
const { RosProto } = require("../../ros_msg_utils/RosProto");

class CustomManager {
    static getInstance() {
        if (!CustomManager.instance) {
            CustomManager.instance = new CustomManager();
        }
        return CustomManager.instance;
    }
    constructor() {
        this.editing = null;
        this.nodes = [];
        this.messages = [];

        this.resetEditing(null, true);
    }
    reloadNode(dest) {
        if (dest.type == "node") {
            let fn = ProjectConfig.WORKSPACE_PATH + "/" + ProjectConfig.PROJECT_NAME + "/" + dest.name + ".py";
            RosProto.customNodeOperate(dest.name, dest.token, "reload", fn, dest.code);
        }
    }
    stopNode(dest) {
        if (dest.type == "node") {
            let fn = ProjectConfig.WORKSPACE_PATH + "/" + ProjectConfig.PROJECT_NAME + "/" + dest.name + ".py";
            RosProto.customNodeOperate(dest.name, dest.token, "stop", fn, dest.code);
        }
    }
    editNodeCode(dest) {
        if (dest.code == "") {
            dest.code = DefaultNodeCode.getNodeCode("ros1").replace("maestro_node", dest.name);
            this.dosave();
        }
        TextEditor.CodeTextInput.Reset({
            language: "python",
            x: 0,
            y: 0,
            w: Scr.logicw / 2,
            h: Scr.logich,
            defaultValue: dest.code,
            onChange: (value) => { },
            onFinish: (value) => {
                const regex = /rospy\.init_node *\(['"]([a-zA-Z0-9_]+)['"] *\)/;
                const match = value.match(regex);

                if (match && match[1]) {
                    if (match[1] != dest.name) {
                        LogManager.E(`代码中启动节点${match[1]}，与节点名称${dest.name}不一致`)
                    }
                }
                if (dest.code != value) {
                    dest.code = value;
                    this.dosave();
                }
            },
        });
    }
    editMessageCode(dest) {
        let msg = dest.compose();
        TextEditor.CodeTextInput.Reset({
            language: "python",
            x: 0,
            y: 0,
            w: Scr.logicw / 2,
            h: Scr.logich,
            defaultValue: msg,
            onChange: (value) => { },
            onFinish: (value) => {
                if (msg != value) {
                    if (dest.parse(value)) {
                        this.resetEditing(dest, true);
                        this.dosave();
                    }
                }
            },
        });
    }
    onDoubleClick(nom) {
        if (nom.type == "node") {
            this.editNodeCode(nom);
        } else if (nom.type == "message") {
            this.editMessageCode(nom);
        }
    }
    resetEditing(dest, force = false) {
        if (!force && this.editing == dest) {
            return;
        }
        AttributeArea.gi().clear();
        AttributeArea.gi().addTitle("自定义节点/消息");
        AttributeArea.gi().addGap(1);
        if (dest == null) {
            AttributeArea.gi().addButton({
                label: "创建自定义节点",
                callback: () => {
                    this.nodes.push(new CustomNode(this.newName("node")));
                    this.resetEditing(this.nodes[this.nodes.length - 1], true);
                    this.dosave();
                }
            });
            AttributeArea.gi().addGap(1);
            AttributeArea.gi().addButton({
                label: "创建自定义消息",
                callback: () => {
                    this.messages.push(new CustomMessage(this.newName("message")));
                    this.resetEditing(this.messages[this.messages.length - 1], true);
                    this.dosave();
                }
            });
        } else if (dest.type == "node") {
            AttributeArea.gi().addInput({
                label: "节点名称：",
                default: dest.name,
                callback: (value) => {
                    dest.name = value;
                    this.dosave();
                }
            });
            AttributeArea.gi().addGap(1);

            AttributeArea.gi().addButton({
                label: "编辑节点代码",
                callback: () => {// todo 启动Monaco编辑节点代码
                    this.editNodeCode(dest);
                }
            });
            AttributeArea.gi().addGap(1);
            AttributeArea.gi().addButton({
                label: "启动/停止节点",
                callback: () => {
                    let fn = ProjectConfig.WORKSPACE_PATH + "/" + ProjectConfig.PROJECT_NAME + "/" + dest.name + ".py";
                    if (dest.running) {
                        RosProto.customNodeOperate(dest.name, dest.token, "stop", fn, dest.code);
                    } else { // 在板子上执行python代码启动节点
                        RosProto.customNodeOperate(dest.name, dest.token, "start", fn, dest.code);
                    }
                }
            });
            AttributeArea.gi().addGap(3);
            AttributeArea.gi().addButton({
                label: "删除节点",
                callback: () => {
                    let idx = this.nodes.indexOf(dest);
                    if (idx >= 0) {
                        this.nodes.splice(idx, 1);
                        this.resetEditing(null, true);
                    }
                }
            });
        } else if (dest.type == "message") {
            AttributeArea.gi().addInput({
                label: "消息名称：",
                default: dest.name,
                callback: (value) => {
                    dest.name = value;
                    this.dosave();
                }
            });
            AttributeArea.gi().addGap(1);
            let pl = dest.compose().split("\n");
            AttributeArea.gi().addList({
                label: `参数表(${dest.param.length})：`,
                list: pl,
                callback: (value) => {
                }
            });
            AttributeArea.gi().addGap(1);
            AttributeArea.gi().addButton({
                label: "使用模板添加参数",
                callback: () => {
                    let k = EnvManager.getInstance().msgKeys[0];
                    this.tmpParam = {
                        pkg_type: k,
                        type: EnvManager.getInstance().msgValues[k][0],
                        name: "",
                        is_const: false,
                        const_value: "",
                        is_array: false,
                        array_size: 0,
                    };
                    this.messageAddParam();
                }
            });
            AttributeArea.gi().addGap(1);
            AttributeArea.gi().addButton({
                label: "直接编辑消息文件",
                callback: () => {
                    this.editMessageCode(dest);
                }
            });
            AttributeArea.gi().addGap(1);
            AttributeArea.gi().addButton({
                label: "编译消息",
                callback: () => {
                }
            });
            AttributeArea.gi().addGap(3);
            AttributeArea.gi().addButton({
                label: "删除消息",
                callback: () => {
                    let idx = this.messages.indexOf(dest);
                    if (idx >= 0) {
                        this.messages.splice(idx, 1);
                        this.resetEditing(null, true);
                    }
                }
            });
        }
        AttributeArea.gi().flush();
        this.editing = dest;
    }
    onDraw() {
        for (let node of this.nodes) {
            node.onDraw(0, 0, this.editing == node);
        }
        for (let message of this.messages) {
            message.onDraw(0, 0, this.editing == message);
        }
    }
    onTouch(msg, x, y) {
        for (let node of this.nodes) {
            if (node.onTouch(msg, x, y)) {
                return true;
            }
        }
        for (let message of this.messages) {
            if (message.onTouch(msg, x, y)) {
                return true;
            }
        }
        if (msg != 2 && this.editing) {
            this.resetEditing(null, true);
        }
        return false;
    }
    newName(middle) {
        for (let i = 1; i < 1000; i++) {
            let name = "custom_" + middle + "_" + i;
            for (let node of this.nodes) {
                if (node.name == name) {
                    name = null;
                    break;
                }
            }
            if (name) {
                for (let message of this.messages) {
                    if (message.name == name) {
                        name = null;
                        break;
                    }
                }
                if (name) {
                    return name;
                }
            }
        }
    }
    doload(nodes, messages) {
        this.nodes = [];
        for (let name in nodes) {
            let node = nodes[name];
            let n = new CustomNode(node.name);
            n.token = node.token;
            n.x = node.x;
            n.y = node.y;
            n.code = node.code;
            this.nodes.push(n);
        }
        this.messages = [];
        for (let name in messages) {
            let message = messages[name];
            let m = new CustomMessage(message.name);
            m.token = message.token;
            m.x = message.x;
            m.y = message.y;
            m.param = [];
            for (let param of message.param) {
                m.param.push(JSON.parse(param));
            }
            this.messages.push(m);
        }
    }
    dosave() {
        ProjectConfig.getInstance().setCustomAttr();
    }
    messageAddParam() {
        if (this.editing && this.editing.type == "message") {
            AttributeArea.gi().clear();
            AttributeArea.gi().addTitle("自定义节点/消息");
            AttributeArea.gi().addGap(1);

            AttributeArea.gi().addDropdown({
                label: "包类型",
                list: EnvManager.getInstance().msgKeys,
                default: this.tmpParam.pkg_type,
                callback: (value) => {
                    this.tmpParam.pkg_type = value;
                    this.messageAddParam();
                }
            });
            AttributeArea.gi().addDropdown({
                searchId: "",
                label: "类型",
                list: EnvManager.getInstance().msgValues[this.tmpParam.pkg_type],
                default: this.tmpParam.type,
                callback: (value) => {
                    this.tmpParam.type = value;
                }
            });
            AttributeArea.gi().addInput({
                label: "参数名称：",
                default: this.tmpParam.name,
                callback: (value) => {
                    this.tmpParam.name = value;
                }
            });
            AttributeArea.gi().addGap(1);

            AttributeArea.gi().addCheckbox({
                label: "常量",
                default: this.tmpParam.is_const,
                callback: (value) => {
                    this.tmpParam.is_const = value;
                    this.messageAddParam();
                }
            });
            if (this.tmpParam.is_const) {
                AttributeArea.gi().addInput({
                    label: "常量值：",
                    default: this.tmpParam.const_value,
                    callback: (value) => {
                        this.tmpParam.const_value = value;
                    }
                });
                AttributeArea.gi().addGap(1);
            }
            AttributeArea.gi().addGap(1);

            AttributeArea.gi().addCheckbox({
                label: "数组",
                default: this.tmpParam.is_array,
                callback: (value) => {
                    this.tmpParam.is_array = value;
                    this.messageAddParam();
                }
            });
            if (this.tmpParam.is_array) {
                AttributeArea.gi().addInput({
                    label: "数组大小：",
                    default: this.tmpParam.array_size,
                    callback: (value) => {
                        this.tmpParam.array_size = value;
                    }
                });
                AttributeArea.gi().addGap(1);
            }
            AttributeArea.gi().addGap(3);

            AttributeArea.gi().addButton({
                label: "添加",
                callback: () => {
                    // todo 检测参数是否符合规范
                    this.editing.param.push(this.tmpParam);
                    this.resetEditing(this.editing, true);
                    this.dosave();
                }
            });

            AttributeArea.gi().flush();
        }
    }
    search(token) {
        for (let node of this.nodes) {
            if (node.token == token) {
                return node;
            }
        }
        for (let message of this.messages) {
            if (message.token == token) {
                return message;
            }
        }
        return null;
    }
    searchByName(name) {
        for (let node of this.nodes) {
            if (node.name == name) {
                return node;
            }
        }
        return null;
    }
}

// addTitle                         div_title     att_title
// addImage    
// addDotLine
// addTopInput                 input_text_readonly_top
// addInput                       input_text_readonly
// addValidatorInput         input_text_readonly
// addTextArea                  input_text_readonly
// addButton                     button_click
// addLabelButton             label_button_click
// addButtonDelete           button_click_delete
// addSelect                      input_text_readonly
// addGap

module.exports = {
    CustomManager
};